﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Comp376A2_9609695
{
    /// <summary>
    /// A level in the game
    /// </summary>
    public class Level : Drawable
    {
        // The tiles are simply a 2D array
        private byte[,] tiles;
        private Rectangle field;
        private Texture2D tunnels;

        public int X
        {
            get { return field.X; }
            set { field.X = value; }
        }
        public int Y
        {
            get { return field.Y; }
            set { field.Y = value; }
        }
        public int Width
        {
            get { return field.Width; }
            set { field.Width = value; }
        }
        public int Height
        {
            get { return field.Height; }
            set { field.Height = value; }
        }

        public Level(Texture2D tunnels)
        {
            tiles = new byte[12,13];
            for (int i = 0; i != 12; i++)
            {
                for (int j = 1; j != 13; j++)
                {
                    tiles[i, j] = 0;
                }
            }
            for (int i = 0; i != 12; i++)
            {
                tiles[i, 0] = 0xFF;
            }
            tiles[5, 6] = 255;
            field = new Rectangle();
            this.tunnels = tunnels;
        }

        public void setField(int x, int y, int width, int height)
        {
            field = new Rectangle(x, y, width, height);
        }

        public void setField(Rectangle field)
        {
            this.field = field;
        }

        public Vector2 getGridPos(int x, int y)
        {
            return new Vector2(x / 16, y / 16);
        }

        public byte getCellAt(int x, int y)
        {
            return getCell(x / 16, y / 16);
        }

        public byte getCell(int x, int y)
        {
            if (x >= 12 || y >= 13 || x < 0 || y < 0)
                return 0;
            return tiles[x, y];
        }

        public Texture2D getImage()
        {
            return tunnels;
        }

        public void draw(SpriteBatch renderer)
        {
            for (int i = 0; i != 12; i++)
            {
                for (int j = 1; j != 13; j++)
                {
                    byte tile = tiles[i, j];
                    if (tile == 0)
                        continue;
                    Vector2 pos = new Vector2(field.X + i*16, field.Y + j*16);
                    // If a tile is completely dug, just draw a black box
                    if (tile == 0x0F)
                        renderer.Draw(tunnels, pos, new Rectangle(16, 0, 16, 16), Color.White);
                    else
                    {
                        // Start off drawing a circle to make the corners round
                        renderer.Draw(tunnels, pos, new Rectangle(0, 0, 16, 16), Color.White);
                        /* Cells draw a tunnel piece on every missing wall.
                         * Each wall has an associated bit
                         * Cell walls are laid out like so:
                         *    2
                         *  1   4
                         *    8
                         */   
                        if ((tile & 1) != 0)
                        {
                            if (i != 0 && (tiles[i - 1, j] & 4) != 0)
                                renderer.Draw(tunnels, new Vector2(pos.X, pos.Y), new Rectangle(0, 16, 8, 16), Color.White);
                        }
                        if ((tile & 4) != 0)
                        {
                            if (i != 11 && (tiles[i + 1, j] & 1) != 0)
                                renderer.Draw(tunnels, new Vector2(pos.X + 8, pos.Y), new Rectangle(0, 16, 8, 16), Color.White);
                        }
                        if ((tile & 2) != 0)
                        {
                            if ((tiles[i, j - 1] & 8) != 0)
                                renderer.Draw(tunnels, new Vector2(pos.X, pos.Y), new Rectangle(16, 16, 16, 8), Color.White);
                        }
                        if ((tile & 8) != 0)
                        {
                            if (j != 12 && (tiles[i, j + 1] & 2) != 0)
                                renderer.Draw(tunnels, new Vector2(pos.X, pos.Y + 8), new Rectangle(16, 16, 16, 8), Color.White);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Return whether or not the part of the cell at that position is still filled
        /// </summary>
        /// <param name="x">X position to check</param>
        /// <param name="y">Y position to check</param>
        /// <returns>True if the wall is filled</returns>
        public bool isFilled(int x, int y)
        {
            x -= field.X;
            y -= field.Y;
            // Return false if the outside of the level being checked
            if (y < 16)
                return false;
            if (x <= 0 || y <= 0 || x >= field.Width || y >= field.Height)
                return true;
            byte tile = tiles[x/16, y/16];
            int tx = x%16;
            int ty = y%16;
            // Check the side the coordinates are on and return if that wall is still filled
            if (ty == 0)
            {
                if (tx < 8)
                    return ((tile & 1) == 0);
                return ((tile & 4) == 0);
            }
            else if (tx == 0)
            {
                if (ty < 8)
                    return ((tile & 2) == 0);
                return ((tile & 8) == 0);
            }
            else return true;
        }

        /// <summary>
        /// Dig out the cell wall at a position
        /// </summary>
        /// <param name="x">The X position to dig</param>
        /// <param name="y">The Y position to dig</param>
        /// <returns>True if something was dug</returns>
        public bool dig(int x, int y)
        {
            bool result = false;
            x -= field.X;
            y -= field.Y;
            // Don't dig outside the level
            if (x < 0 || y < 0 || x >= field.Width || y >= field.Height)
                return false;
            byte tile = tiles[x / 16, y / 16];
            int tx = x % 16;
            int ty = y % 16;
            // Get the wall to be dug and force its bit to 1. Return whether or not that bit was 1 before
            if (ty == 0 && x >=4)
            {
                if (tx < 8)
                {
                    result = (tile & 1) == 0;
                    tile |= 1;
                }
                if (tx >= 8)
                {
                    result = (tile & 4) == 0;
                    tile |= 4;
                }
            }
            else if (tx == 0)
            {
                if (ty < 8)
                {
                    result = (tile & 2) == 0;
                    tile |= 2;
                }
                if (ty >= 8)
                {
                    result = (tile & 8) == 0;
                    tile |= 8;
                }
            }
            tiles[x / 16, y / 16] = tile;
            return result;
        }

        /// <summary>
        /// Set a cell to a specific value
        /// </summary>
        /// <param name="x">X index of the cell</param>
        /// <param name="y">Y index of the cell</param>
        /// <param name="value">value</param>
        public void setCell(int x, int y, byte value)
        {
            if (x < 12 && y < 13)
                tiles[x, y] = value;
        }
    }
}
